#!/bin/bash

check_4() {
  logit ""
  local id="4"
  local desc="容器镜像和构建文件"
  checkHeader="$id - $desc"
  info "$checkHeader"
  startsectionjson "$id" "$desc"
}

check_4_1() {
  local id="4.1"
  local desc="确保容器的运行用户已经被创建（自动）"
  local remediation="应该确保每个容器镜像的Dockerfile包含信息: USER <username or ID>. 如果在容器基础镜像中没有创建特定用户，则在USER指令之前添加useradd命令以添加特定用户"
  local remediationImpact="以非root用户身份运行可能会带来挑战。在这种情况下，应注意确保运行包含进程的用户可以根据他们的要求对绑定目录进行读写。"
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  # 如果 containers 为空，则没有正在运行的容器
  if [ -z "$containers" ]; then
    info -c "$check"
    info "     * 无容器运行"
    logcheckresult "INFO" "无容器运行"
    return
  fi
  # 我们有一些容器正在运行，请将失败标志设置为0，检查用户
  fail=0
  # 使循环分隔符成为符合POSIX标准的新行
  set -f; IFS=$'
  '
  root_containers=""
  for c in $containers; do
    user=$(docker inspect --format 'User={{.Config.User}}' "$c")

    if [ "$user" = "User=0" ] || [ "$user" = "User=root" ] || [ "$user" = "User=" ] || [ "$user" = "User=[]" ] || [ "$user" = "User=<no value>" ]; then
      # 如果是第一个容器，则测试失败
      if [ $fail -eq 0 ]; then
        warn -s "$check"
        warn "     * 以root运行: $c"
        root_containers="$root_containers $c"
        fail=1
        continue
      fi
      warn "     * 以root运行: $c"
      root_containers="$root_containers $c"
    fi
  done
  # 我们检查了所有容器，发现没有一个以root运行
  if [ $fail -eq 0 ]; then
    pass -s "$check"
    logcheckresult "PASS"
    return
  fi
  logcheckresult "WARN" "running as root" "$root_containers"
  # 使循环分隔符返回到空间
  set +f; unset IFS
}

check_4_2() {
  local id="4.2"
  local desc="确保容器只能运行可信任的基础镜像"
  local remediation="1.配置和使用Docker内容信任；2.查看每个Docker映像的历史记录以评估其风险，具体取决于使用它部署的应用程序的敏感性；3.定期扫描Docker镜像以查找其依赖关系中的漏洞。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_3() {
  local id="4.3"
  local desc="确保容器中不安装没有必要的软件包"
  local remediation="容器往往是操作系统的最简的版本，不要安装任何不需要的软件。安装不必要的软件可能会增加容器的攻击风险。因此，除了容器的真正需要的软件之外，不要安装其他多余的软件。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_4() {
  local id="4.4"
  local desc="确保扫描镜像漏洞并且构建包含安全补丁的镜像"
  local remediation="应重建映像以确保使用最新版本的基础映像，以将操作系统补丁级别保持在适当的级别。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_5() {
  local id="4.5"
  local desc="启用 docker 内容信任（自动）"
  local remediation="要在bash shell中启用内容信任，请输入以下命令：export DOCKER_CONTENT_TRUST=1，或者在配置文件中设置此环境变量，以便每次登录时启用内容信任。"
  local remediationImpact="这些签名允许客户端验证特定镜像标签的完整性和发布者。"
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  if [ "$DOCKER_CONTENT_TRUST" = "1" ]; then
    pass -s "$check"
    logcheckresult "PASS"
    return
  fi
  warn -s "$check"
  logcheckresult "WARN"
}

check_4_6() {
  local id="4.6"
  local desc="将 HEALTHCHECK 说明添加到容器镜像（自动）"
  local remediation="在Docker容器镜像中添加HEALTHCHECK指令以对正在运行的容器执行健康检查。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  fail=0
  no_health_images=""
  for img in $images; do
    if docker inspect --format='{{.Config.Healthcheck}}' "$img" 2>/dev/null | grep -e "<nil>" >/dev/null 2>&1; then
      if [ $fail -eq 0 ]; then
        fail=1
        warn -s "$check"
      fi
      imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null)
      if ! [ "$imgName" = '[]' ]; then
        warn "     * No Healthcheck found: $imgName"
        no_health_images="$no_health_images $imgName"
      else
        warn "     * No Healthcheck found: $img"
        no_health_images="$no_health_images $img"
      fi
    fi
  done
  if [ $fail -eq 0 ]; then
    pass -s "$check"
    logcheckresult "PASS"
    return
  fi
  logcheckresult "WARN" "Images w/o HEALTHCHECK" "$no_health_images"
}

check_4_7() {
  local id="4.7"
  local desc="不在 dockerfile 中单独使用更新命令"
  local remediation="在Dockerfile添加更新指令将缓存更新的层。稍后使用相同的指令构建任何镜像时，将使用先前缓存的更新图层。可能会阻止任何新的更新应用于以后的构建。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  fail=0
  update_images=""
  for img in $images; do
    if docker history "$img" 2>/dev/null | grep -e "update" >/dev/null 2>&1; then
      if [ $fail -eq 0 ]; then
        fail=1
        info -c "$check"
      fi
      imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null)
      if ! [ "$imgName" = '[]' ]; then
        info "     * Update instruction found: $imgName"
        update_images="$update_images $imgName"
      fi
    fi
  done
  if [ $fail -eq 0 ]; then
    pass -c "$check"
    logcheckresult "PASS"
    return
  fi
  logcheckresult "INFO" "Update instructions found" "$update_images"
}

check_4_8() {
  local id="4.8"
  local desc="镜像中删除 setuid 和 setgid 权限"
  local remediation="应该只对需要它们的可执⾏⽂件授予setuid和setgid权限。可在构建时通过在Dockerfile中添加以下命令来删除这些权限，最好添加在 Dockerfile的末尾：RUN find / -perm /6000 -type f -exec chmod a-s {} \; || true"
  local remediationImpact="以上命令会导致依赖setuid或setgid权限（包括合法权限）的可执行文件无法执行。因此，应该小心地修改命令以满足要求，以免过度降低合法程序的权限。因此，在进行此类修改之前，应该谨慎行事并仔细检查所有流程，以避免中断。"
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_9() {
  local id="4.9"
  local desc="在 dockerfile 中使用 copy 而不是 add"
  local remediation="在Dockerfile中使用COPY指令而不是ADD指令"
  local remediationImpact="如果应用程序需要用ADD指令一部分的功能，例如，如果需要从远程 URL检索文件，则在实现此控制时需要小心。"
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  fail=0
  add_images=""
  for img in $images; do
    if docker history --format "{{ .CreatedBy }}" --no-trunc "$img" | \
      sed '$d' | grep -q 'ADD'; then
      if [ $fail -eq 0 ]; then
        fail=1
        info -c "$check"
      fi
      imgName=$(docker inspect --format='{{.RepoTags}}' "$img" 2>/dev/null)
      if ! [ "$imgName" = '[]' ]; then
        info "     * ADD in image history: $imgName"
        add_images="$add_images $imgName"
      fi
    fi
  done
  if [ $fail -eq 0 ]; then
    pass -c "$check"
    logcheckresult "PASS"
    return
  fi
  logcheckresult "INFO" "Images using ADD" "$add_images"
}

check_4_10() {
  local id="4.10"
  local desc="确保涉密信息不存储在 dockerfile"
  local remediation="不要在Dockerfiles中存储任何涉密信息。如果在构建过程中需要机密，请使用机密管理工具，例如Docker附带的buildkit构建器。"
  local remediationImpact="Docker镜像构建需要一个适当的秘密管理过程。若必须使用，需要制定相应的措施。"
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_11() {
  local id="4.11"
  local desc="确保仅安装已经验证的软件包"
  local remediation="应该选择使用安全包分发机制来确保软件包的真实性。使用GPG密钥下载和验证所选择的软件包或任何其他安全软件包分发机制。"
  local remediationImpact="None."
  local check="$id - $desc"
  starttestjson "$id" "$desc"

  note -c "$check"
  logcheckresult "NOTE"
}

check_4_end() {
  endsectionjson
}
